﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;

namespace UltimateNavigationFramework.Extensions
{
    public static class DependencyObjectExtensions
    {
        /// <summary>
        /// Finds the child of the dependency object by type
        /// </summary>
        /// <typeparam name="TChildType"></typeparam>
        /// <param name="dependencyObject"></param>
        /// <returns></returns>
        public static TChildType FindChild<TChildType>(this DependencyObject dependencyObject) where TChildType : DependencyObject
        {
            if (dependencyObject == null)
            {
                throw new ArgumentNullException("dependencyObject");
            }

            int childCount = VisualTreeHelper.GetChildrenCount(dependencyObject);

            for (int i = 0; i < childCount; ++i)
            {
                DependencyObject child = VisualTreeHelper.GetChild(dependencyObject, i);

                if (child is TChildType)
                {
                    return (TChildType)child;
                }

                child = child.FindChild<TChildType>();

                if (child != null)
                {
                    return (TChildType)child;
                }
            }

            return null;
        }

        /// <summary>
        /// Finds the children of the Dependency Object by type
        /// </summary>
        /// <typeparam name="TChildType"></typeparam>
        /// <param name="dependencyObject"></param>
        /// <returns></returns>
        public static IEnumerable<TChildType> FindFirstOccuringChildren<TChildType>(this DependencyObject dependencyObject) where TChildType : DependencyObject
        {
            if (dependencyObject == null)
            {
                throw new ArgumentNullException("dependencyObject");
            }

            var children = new List<TChildType>();

            int childCount = VisualTreeHelper.GetChildrenCount(dependencyObject);

            for (int i = 0; i < childCount; ++i)
            {
                DependencyObject child = VisualTreeHelper.GetChild(dependencyObject, i);

                if (child is TChildType)
                {
                    children.Add((TChildType)child);
                }
            }

            if (children.Count == 0)
            {
                for (int i = 0; i < childCount; ++i)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(dependencyObject, i);

                    if (child.FindChild<TChildType>() != null)
                    {
                        children.AddRange(child.FindFirstOccuringChildren<TChildType>());
                    }
                }
            }

            return children;
        }

        /// <summary>
        /// Finds all the children of the Dependency Object by type
        /// Will be ordered by lowest level child first
        /// </summary>
        /// <typeparam name="TChildType"></typeparam>
        /// <param name="dependencyObject"></param>
        /// <returns></returns>
        public static IEnumerable<TChildType> FindChildren<TChildType>(this DependencyObject dependencyObject) where TChildType : DependencyObject
        {
            if (dependencyObject == null)
            {
                throw new ArgumentNullException("dependencyObject");
            }

            var children = new List<TChildType>();

            int childCount = VisualTreeHelper.GetChildrenCount(dependencyObject);

            // Always get the children first
            for (int i = 0; i < childCount; ++i)
            {
                DependencyObject child = VisualTreeHelper.GetChild(dependencyObject, i);

                children.AddRange(child.FindChildren<TChildType>());
            }

            // Add the current level item
            for (int i = 0; i < childCount; ++i)
            {
                DependencyObject child = VisualTreeHelper.GetChild(dependencyObject, i);

                if (child is TChildType)
                {
                    children.Add((TChildType)child);
                }
            }

            return children;
        }
    }
}
